/*
--------------------------------------------------------------------------------
				Ak-Lib. 
			 Project       	: Ak-Lib
			 Group         	: Ak-Lib R&D Team
			 Author        	: Ocean.Wang
			 Create Date 	: 05-01-2010
--------------------------------------------------------------------------------
Code Name 	:  A.K 	~v~
Description	:  Yet Another Cross-Platform Lib.
Cautions    	:
--------------------------------------------------------------------------------
Change History:
Version		: 0.1
Building 		: 05/01/10
10-05.01 first draft, build, debug and test.
--------------------------------------------------------------------------------
*/

#include "ak_link.h"

static 	int 	_gTraceLvl_Link = TRACELVL_WARNING;

int 	SIOLink_SetTraceLvl(int lvl)
{
	int 	ret = -1;
	
	switch(lvl){
		case TRACELVL_FATAL:
		case TRACELVL_ERROR:
		case TRACELVL_WARNING:
		case TRACELVL_NORMAL:
		case TRACELVL_DEBUG:
		case TRACELVL_ALL:
			_gTraceLvl_Link = lvl;
			ret = 0;
			break;
		default:
			break;
	}

	return ret;
}

int 	SIOLink_GetTraceLvl(void)
{
	return  _gTraceLvl_Link;
}

/* -----------------------------------------------------------------------------
** 	Global Link ID. (Use the Atomic OP)
*/
static 	unsigned long 	_gCasId_ioLink = 0;
static	__inline	unsigned long	_ioLink_GetCasId(void)
{
	unsigned long casId;
	casId = ATOMIC_INC(&_gCasId_ioLink);
	/* CasId 0 is Special as Invalid */
	if(casId == 0){
		casId = ATOMIC_INC(&_gCasId_ioLink);
	}
	return casId;
}

int 	ioLink_SetIoFunc(_t_ioLinkCi * pLink, _t_ioFunc * ioFunc)
{
	if(pLink && ioFunc){
		pLink->ioFunc = ioFunc;
		return 0;
	}
	return -1;
}

int 	ioLink_SetCookie(_t_ioLinkCi * pLink, void * pCookie)
{
	if(pLink){
		if((pLink->bSche == _F_YES)){
			pLink->hUser = pCookie;
			return 0;
		}else{
			/* Add support for IOLink Down */
			if(pCookie == NULL){
				pLink->hUser = NULL;
				return 0;
			}
			
		}
	}

	return -1;
}

int 	ioLink_GetCasId(_t_ioLinkCi * pLink, unsigned long * pCasId)
{
	if(pLink && pCasId){
		*pCasId = 0;
		if((pLink->bSche == _F_YES)){
			*pCasId = (pLink->casId);
			return 0;
		}
	}

	return -1;
}

int 	ioLink_CmpCasId(_t_ioLinkCi * pNew, _t_ioLinkCi * pOld, unsigned long lCasId)
{
	if(pNew && pOld && (pNew == pOld) ){
		if((pNew->casId == lCasId)){
			return 0;
		}
	}

	return -1;
}

int 	ioLink_Init(_t_ioLinkCi * pLink, S_SOCKET fd, _e_sockType type, _t_sockFunc * pSFunc, unsigned long eCase)
{
	if(pLink && (fd != S_SOCKET_INVALID)){
		RSI_InitNode(&(pLink->txNode));
		RSI_InitNode(&(pLink->rxNode));
		
		/* Socket */
		pLink->eUser.fd 	= fd;
		pLink->eUser.type 	= type;
		pLink->eUser.pSFunc = pSFunc;
		pLink->eUser.eCase 	= eCase;
		pLink->eUser.pCb 	= NULL;
		pLink->eUser.state 	= SOCKST_NUL;
#if _TARGET_OS == _OS_WIN
		pLink->eUser.event 	= WSA_INVALID_EVENT;
		pLink->eUser.index 	= -1;
#endif

#if 0
		pLink->local.b64 	= pLocal->b64;
		pLink->remote.b64	= pRemote->b64;
#endif

		/* Sche */
		pLink->pPool 	= NULL;
		pLink->casId 	= _ioLink_GetCasId();
		pLink->pJob 	= NULL;		/* We must Init it Mannually */
		pLink->bSche 	= _F_NO;

		/* User */
		pLink->cbUser	= NULL;
		pLink->hUser	= NULL;
		pLink->lUser 	= -1;

		/* IO Func */
		pLink->ioFunc 	= NULL;
		return 0;
	}


	return -1;
}

static 	int 	_ioLink_Fini(_t_ioLinkCi * pLink)
{
	if(pLink){
		/* Socket */
		pLink->eUser.fd 		= S_SOCKET_INVALID;
		pLink->eUser.pCb 		= NULL;
		pLink->eUser.eCase 		= 0;
#if _TARGET_OS == _OS_WIN
		pLink->eUser.event 	= WSA_INVALID_EVENT;
		pLink->eUser.index 	= -1;
#endif

#if 0
		_IPPORT_INIT(&(pLink->local));
		_IPPORT_INIT(&(pLink->remote));
#endif
		pLink->pJob 	= NULL;		/* We must Init it Mannually */
		pLink->bSche 	= _F_NO;

		pLink->casId	= 0;
		
		/* User */
		pLink->cbUser	= NULL;
		pLink->hUser	= NULL;
		pLink->lUser 	= -1;

		/* IO Func */
		pLink->ioFunc 	= NULL;
		return 0;
	}

	return -1;
	
}

/*
--------------------------------------------------------------------------------
** 	Link Tree Operations:
*/
static 	int 					_ioTree_Comp(_t_rbnode * node1, _t_rbnode * node2)
{
	_t_ioLinkCi 	* pLink1;
	_t_ioLinkCi 	* pLink2;
	
	pLink1 = _O_PARENT(_t_ioLinkCi, mi.kNode, node1);
	pLink2 = _O_PARENT(_t_ioLinkCi, mi.kNode, node2);

	if(pLink1->casId > pLink2->casId){
		return _TREE_GREAT;
	}
	else if(pLink1->casId < pLink2->casId){
		return _TREE_LESS;
	}
	return _TREE_EQUAL;
}

static 	__inline 	_t_ioLinkCi * _ioTree_Add(_t_ioLinkJob * pLinkJob, _t_ioLinkCi * pLink)
{
	_t_rbroot * tRoot;
	_t_rbnode * tNode;
	_t_ioLinkCi * pTemp;
	
	pTemp = NULL;
	tRoot = &(pLinkJob->linkTree);
	tNode = rb_insert(tRoot, &(pLink->mi.kNode), _ioTree_Comp);	
	if(tNode){
		rb_replace(tRoot, tNode, &(pLink->mi.kNode)); 	
		pTemp = _O_PARENT(_t_ioLinkCi, mi.kNode, tNode);
	}
	
	return pTemp;
}

static 	__inline 	_t_ioLinkCi * _ioTree_Find(_t_ioLinkJob * pLinkJob, unsigned long casId)
{
	_t_ioLinkCi 	temp;
	_t_rbnode	* 	tNode;
	_t_rbroot	* 	tRoot;
	_t_ioLinkCi	* 	pLink;

	temp.casId = casId;
	pLink = NULL;
	
	tRoot = &(pLinkJob->linkTree);
	tNode = rb_find(tRoot, &(temp.mi.kNode), _ioTree_Comp);
	if(tNode){
		pLink = _O_PARENT(_t_ioLinkCi, mi.kNode, tNode);
	}
	return pLink;

}

static 	__inline 	_t_ioLinkCi * _ioTree_Del(_t_ioLinkJob * pLinkJob, _t_ioLinkCi * pLink)
{
	_t_ioLinkCi * pTemp;
	pTemp = NULL;

	/* Key-Value */
	pTemp = _ioTree_Find(pLinkJob, pLink->casId);
	if(pTemp){
		rb_erase(&(pLinkJob->linkTree), &(pLink->mi.kNode));
	}
	return pTemp;
}



/* =====================================================================
** 	Link Add & Del.
*/
/*
**	If IoLink Node Has Not been Add to the Tree, Call ioLink_Rm 
*/
void 	ioLink_Rm(_t_ioLinkCi * pLink)
{
	_t_sockFunc * pSFunc;

	/* Close Handle */
	if(pLink->eUser.fd != S_SOCKET_INVALID){
		pSFunc = pLink->eUser.pSFunc;
		/* Get the Sock Func -- Sock Type */
		if(pSFunc && (pSFunc->close)){
			pSFunc->close(pLink->eUser.fd);
		}
		pLink->eUser.state = SOCKST_NUL;
	}

	SYS_TRACE(_gTraceLvl_Link, TRACELVL_ERROR, "Link RM :%p ->%lu %d\n", 
			pLink, 
			pLink->casId, 
			pLink->eUser.fd);

	/* Fini & Free */
	_ioLink_Fini(pLink);
	MPool_FreeSi(pLink->pPool, pLink);
	return;
}

/*
** 	If IoLink Node Has Already been Add to the Tree, Call _ioLink_Del 
*/
static 	int 	_ioLink_Del(_t_ioLinkJob  * pLinkJob, _t_ioLinkCi * pLink)
{
	_t_ioLinkCi 	* pTemp;	/* Check Tree */
	_t_sysEvSel   	* pEvSel;
	

	if(pLink->eUser.state != SOCKST_NUL){
		pEvSel = pLinkJob->pEvSel;
		pTemp = _ioTree_Del(pLinkJob, pLink);
		if(pTemp == pLink){
			/* Del 4 EvSel */
			#if 0
			SYS_TRACE(_gTraceLvl_Link, TRACELVL_ERROR, "Link Del:%p ->%p %d (%p)\n", 
					pLink, 
					pEvSel, 
					pLink->eUser.fd, 
					pLink->pJob);
			#endif
			EvSel_Del(pEvSel, &(pLink->eUser));
			pLink->bSche = _F_NO;
			
			RSI_DeQue(&(pLinkJob->txQ), &(pLink->txNode));
			RSI_DeQue(&(pLinkJob->rxQ), &(pLink->rxNode));

			/* Resource RM */
			ioLink_Rm(pLink);
			pLinkJob->linkStat -= 1;
			return 0;
		}
		#if 1
		else{
			SYS_TRACE(_gTraceLvl_Link, TRACELVL_ERROR, "Link Del N:%p ->%p %d\n", 
					pLink, 
					pEvSel, 
					pLink->eUser.fd);
		}
		#endif
	}
	
	return -1;
}

static 	int 	_ioLink_Err(_t_ioLinkCi * pLink, int type)
{
	_t_ioCbUser * pCbUser;
	/* Call CbUser's Notify */
	pCbUser = pLink->cbUser;
	if(pCbUser && (pCbUser->cbErr)){
		#if 0
		SYS_TRACE(_gTraceLvl_Link, TRACELVL_ERROR, "Link Error:%p ->%p %ld\n", 
					pLink, 
					pLink->hUser, 
					pLink->lUser);
		#endif
		pCbUser->cbErr(	pLink->hUser, 
						pLink->lUser, 
						pLink,
						type);
		return 0;
	}

	return -1;
}


static 	int 	_ioLink_Add(_t_ioLinkJob  * pLinkJob, _t_ioLinkCi * pLink)
{
	_t_ioLinkCi * pTemp;	/* Check Tree */
	
	_t_sysEvSel   * pEvSel;

	/* Add 2 EvSel */
	pEvSel = pLinkJob->pEvSel;
	if(EvSel_Add(pEvSel, &(pLink->eUser)) == 0){
		/* Add 2 Tree */
		pTemp = _ioTree_Add(pLinkJob, pLink);
		if(pTemp != NULL){
			SYS_TRACE(_gTraceLvl_Link, TRACELVL_ERROR, "Link Add N:%p ->(%d-%d)\n", 
				pLink, 
				pLink->eUser.fd, 
				pTemp->eUser.fd);
			
			/* Execption Handler */
			_ioLink_Err(pLink, ERR_LINK_OP);
			_ioLink_Del(pLinkJob, pTemp);
		}
		/* Sche */
		pLink->bSche 	= _F_YES;
		pLinkJob->linkStat += 1;
		return 0;
	}

	else{
		#if 1
		SYS_TRACE(_gTraceLvl_Link, TRACELVL_ERROR, "Link Add 2 EvSel: Fail %p ->(%d)\n", 
				pLink, 
				pLink->eUser.fd);
		#endif
		/* Execption Handler */
		ioLink_Rm(pLink);
	}
	return -1;
}


/* =====================================================================
** 	Link Connect .
*/
static 	int		_ioLink_Conn(_t_ioLinkCi * pLink)
{
	_t_ioCbUser 	* 	pCbUser;
	_t_ioLinkJob 	* 	pLinkJob;
	S_SOCKET 			sockFd;
	int 				ret, len;
	
	ret = -1;
	/* Non -Block, We check the Socket When Writable. */
	pLinkJob= pLink->pJob;
	sockFd 	= pLink->eUser.fd;
	len 	= sizeof(int);
	getsockopt(sockFd, SOL_SOCKET, SO_ERROR, (void *)(&ret), (unsigned int *)(&len));
	
	if(ret != 0){
		SYS_TRACE(_gTraceLvl_Link, TRACELVL_DEBUG, "Link ConnF:%p (%d) (%d)\n",
				pLink, sockFd, ret);

		/* Exception Handler */
		_ioLink_Err(pLink, ERR_LINK_FD);
		_ioLink_Del(pLinkJob, pLink);
	}
	else{
		SYS_TRACE(_gTraceLvl_Link, TRACELVL_DEBUG, "Link ConnS:%p (%d)\n",
				pLink, sockFd);
		/* Ev Modify */
		pLink->eUser.state 	= SOCKST_OK;
		pLink->eUser.eCase 	= S_ECASE_NORM;
		EvSel_Mod(pLinkJob->pEvSel, &(pLink->eUser));

		ret = 0;
		if((pCbUser = pLink->cbUser) && (pCbUser->cbConn)){
			/* Here We Should Add the Call Back for Accept Event */
			ret = pCbUser->cbConn(	pLink->hUser,
									pLink->lUser,
									pLink);
		}
		if(ret == -1){
			/* Execption Handler */
			_ioLink_Err(pLink, ERR_LINK_OP);
			_ioLink_Del(pLinkJob, pLink);
		}else{
			RSI_EnStdby(&(pLinkJob->txQ), &(pLink->txNode));
		}
		
		
	}
	
	return ret;
}


/* =====================================================================
** 	Tx/Rx OPs.
*/
static 	__inline int 	_ioLink_Tx(_t_ioLinkCi * pLink)
{
	_t_ioCbUser *	pCbUser;
	_t_rsiQue 	* 	pRSI;
	int 			ret;

	ret 		= -1;
	pCbUser 	= pLink->cbUser;
	
	pRSI = &(pLink->pJob->txQ);
	if(	(pLink->bSche == _F_YES) && (pLink->eUser.state == SOCKST_OK)){
		if(pCbUser && (pCbUser->cbTxSche)){
			ret = pCbUser->cbTxSche(pLink->hUser,
									pLink->lUser,
									pLink);
			/* Judge to Schedule */
			/* 
			** ret = -1 : EAGAIN. just Standby.
			**
			** ret =  0 : Succ.
			**
			** ret >  0 : Still something Ready to Tx.
			*/
			if(ret == 0){
				RSI_DeQue(pRSI, &(pLink->txNode));
			}else if(ret == -1){
				RSI_EnStdby(pRSI, &(pLink->txNode));
			}
		}
		else{
			RSI_DeQue(pRSI, &(pLink->txNode));
		}
	}
	
	return ret;
}


static 	__inline int 	_ioLink_Rx(_t_ioLinkCi * pLink)
{
	_t_ioCbUser *	pCbUser;
	_t_rsiQue 	* 	pRSI;
	int 			ret;

	ret 		= -1;
	pCbUser 	= pLink->cbUser;
	pRSI = &(pLink->pJob->rxQ);
	if(	(pLink->bSche == _F_YES) && (pLink->eUser.state == SOCKST_OK)){
		if(pCbUser && (pCbUser->cbRxSche)){
			ret = pCbUser->cbRxSche(pLink->hUser,
									pLink->lUser,
									pLink);
			/* Judge to Schedule */
			/* 
			** ret = -1 : EAGAIN. just Standby.
			**
			** ret =  0 : Succ. 
			**
			** We Always Retry & Recv to EAGAIN.
			*/
			if(ret == -1){
				RSI_EnStdby(pRSI, &(pLink->rxNode));
			}
			else{
				RSI_DeQue(pRSI, &(pLink->rxNode));
			}
		}
		else{
			RSI_EnStdby(pRSI, &(pLink->rxNode));
		}
	}
	
	return ret;
}

static 	int 	_ioLink_TxNode(_t_rsiNode * pNode)
{
	int 			ret;
	_t_ioLinkCi * 	pLink;

	ret = -1;
	if(pNode){
		pLink = _O_PARENT(_t_ioLinkCi, txNode, pNode);
		ret = _ioLink_Tx(pLink);
	}
	return ret;
}

static 	int 	_ioLink_RxNode(_t_rsiNode * pNode)
{
	int 			ret;
	_t_ioLinkCi * 	pLink;

	ret = -1;
	if(pNode){
		pLink = _O_PARENT(_t_ioLinkCi, rxNode, pNode);
		ret = _ioLink_Rx(pLink);
	}
	return ret;
}

/* =====================================================================
** 	Link Job's RSI Schedule.
*/
#define _LINKJOB_PROC_T 		(0X50)
#define _LINKJOB_PROC_X 		(0XA0)

static 	void 	_ioLink_XfStdby(_t_ioLinkJob  * pLinkJob)
{
	_t_rsiQue * pRSI;
	
	/* Here We Check the All Tx & Rx Que to Anti-Lock under Hi-Load. */
	pRSI = &(pLinkJob->rxQ);
	RSI_XfStdby(pRSI, _LINKJOB_PROC_T);

	pRSI = &(pLinkJob->txQ);
	RSI_XfStdby(pRSI, _LINKJOB_PROC_T);

	return;
}

static 	int 	_ioLink_XfReady(_t_ioLinkJob  * pLinkJob)
{
	int 		rxFlag, txFlag;
	_t_rsiQue * pRSI;
	
	/* Process Cycle with Rx & Tx. */
	pRSI = &(pLinkJob->rxQ);
	rxFlag = RSI_XfReady(pRSI, _LINKJOB_PROC_X, _ioLink_RxNode);

	pRSI = &(pLinkJob->txQ);
	txFlag = RSI_XfReady(pRSI, _LINKJOB_PROC_X, _ioLink_TxNode);
	
	
	return (rxFlag | txFlag);
}


/* =====================================================================
** 	Link Job's Proc.
*/
static 	int 	_ioLink_JobProcHi(_t_ioLinkJob * pLinkJob, long lMagic, _t_sysJobNode * pJobNode)
{
	_t_ioLinkCi * pLink;
	_t_sockEvCb * pEvCb;
	
	pLink = (_t_ioLinkCi *)(pJobNode->pParam);
	switch(pJobNode->lParam[0]){
		case S_EVOP_RX:
		#if 0
			SYS_TRACE(_gTraceLvl_Link, TRACELVL_DEBUG, "Link Rx:%p \n",
					pLink);							
		#endif
			if( (pLink->bSche == _F_YES) && (pLink->pJob == pLinkJob)){
				if(pLink->eUser.state == SOCKST_OK){
					pEvCb = pLink->eUser.pCb;
					if(pEvCb->cbIn){
						/* Fast Path */
						pEvCb->cbIn(pLink);
					}
					else{
						RSI_EnReady(&(pLinkJob->rxQ), &(pLink->rxNode));
					}
				}
				else if(pLink->eUser.state == SOCKST_CONN){
					_ioLink_Conn(pLink);
				}
			}
			break;
			
		case S_EVOP_TX:	/* Connect */
		#if 0
			XOS_TRACE(_gTraceLvl_Link, TRACELVL_DEBUG, "Link Tx:%p \n",
					pLink);
		#endif
			if( (pLink->bSche == _F_YES) && (pLink->pJob == pLinkJob)){
				if(pLink->eUser.state == SOCKST_OK) {
					RSI_EnReady(&(pLinkJob->txQ), &(pLink->txNode));
				}
				else if(pLink->eUser.state == SOCKST_CONN){
					_ioLink_Conn(pLink);
				}
			}
			break;
			
		case S_EVOP_ERR:
			if((pLink->bSche == _F_YES) && (pLink->pJob == pLinkJob)){
				/* Exception Handler */
				_ioLink_Err(pLink, ERR_LINK_NETWORK);
				_ioLink_Del(pLinkJob, pLink);
			}
			break;
			
		case S_EVOP_TIMER:
			/* Job Re-Schedule */
			_ioLink_XfStdby(pLinkJob);
			break;
				
		case _LINKOP_DEL:
			if((pLink->bSche == _F_YES) && (pLink->pJob == pLinkJob)){
				_ioLink_Del(pLinkJob, pLink);
			}
			break;
			
		case _LINKOP_ADD:
		#if 0
			SYS_TRACE(_gTraceLvl_Link, TRACELVL_DEBUG, "Link Add:%p \n",
				pLink);
		#endif
			if((pLink->bSche == _F_NO) && (pLink->pJob == pLinkJob)){
				_ioLink_Add(pLinkJob, pLink);
			}
			break;
	
		default:
			break;
	}

	return 0;
}


static 	int 	_ioLink_JobProcLo(_t_ioLinkJob * pLinkJob, long lMagic, _t_sysJobNode * pJobNode)
{
	_t_ioLinkCi * pLink;
	_t_ioCbUser * pCbUsr;
	
	pLink = (_t_ioLinkCi *)(pJobNode->pParam);
	switch(pJobNode->lParam[0]){
		case _LINKOP_MSG:
		case _LINKOP_FUNC:
		case _LINKOP_ASYNC:
			pCbUsr = pLink->cbUser;
			if(pCbUsr && pCbUsr->cbMisc){
				pCbUsr->cbMisc(	pLinkJob, 
								pLink->hUser, pLink->lUser,
								pJobNode);
			}
			break;
		
		default:
			break;
	}

	return 0;
}


/* =====================================================================
** 	Link Job's Init.
*/
#define _LINKJOB_RING_MIN 			256
#define _LINKJOB_RING_BAT_RATIO 	16
#define _LINKJOB_RING_JOB_RATIO 	2
int 	ioLink_JobInit(_t_ioLinkJob  * pLinkJob, 	
								_t_sysSemB  * pWake, 
								_t_sysEvSel * pEvSel,
								unsigned int  ringHi,	
								unsigned int  ringLo)
{
	_t_cbentry	 jProcHi;	
	_t_cbentry	 jProcLo;
	_t_sysJobQ * pJobQ;
	int 		 bat;
	
	/* EvSel */
	pLinkJob->pEvSel = pEvSel;
	
	/* JobQ Init Hi */
	_CBENTRY_INIT(&jProcHi, _ioLink_JobProcHi, pLinkJob, 0);
	pJobQ = &(pLinkJob->jobHi);
	if(ringHi < _LINKJOB_RING_MIN)
		ringHi = _LINKJOB_RING_MIN;

	/* Calc the Bat */
	bat = (ringHi/_LINKJOB_RING_BAT_RATIO);
	if(bat > SJOBQ_BAT_MAX){
		bat = SJOBQ_BAT_MAX;
	}
	SJobQ_Init(pJobQ, pWake, &jProcHi,
				ringHi, bat,  (ringHi * _LINKJOB_RING_JOB_RATIO), 
				sizeof(_t_sysJobNode));

	/* JobQ Init Lo */
	_CBENTRY_INIT(&jProcLo, _ioLink_JobProcLo, pLinkJob, 0);
	pJobQ = &(pLinkJob->jobLo);
	if(ringLo < _LINKJOB_RING_MIN)
		ringLo = _LINKJOB_RING_MIN;
	
	/* Calc the Bat */
	bat = (ringLo/_LINKJOB_RING_BAT_RATIO);
	if(bat > SJOBQ_BAT_MAX){
		bat = SJOBQ_BAT_MAX;
	}
	SJobQ_Init(pJobQ, pWake, &jProcLo,
				ringLo, bat,  (ringLo * _LINKJOB_RING_JOB_RATIO), 
				sizeof(_t_sysJobNode));

	/* Tree & RSI */
	pLinkJob->linkStat = 0;
	RB_CLEAR_ROOT(&(pLinkJob->linkTree));

	SYS_INIT_CS(&(pLinkJob->lock));
	RSI_InitQue(&(pLinkJob->txQ), &(pLinkJob->lock));
	RSI_InitQue(&(pLinkJob->rxQ), &(pLinkJob->lock));

	return 0;	
}


int 	ioLink_JobSche(_t_ioLinkJob * pLinkJob, _t_sysJobNode ** ppJobNode)
{
	_t_sysJobQ 		* pJobQHi;
	_t_sysJobQ 		* pJobQLo;

__LABEL_IOLINK_REDO:
	pJobQHi 	= &(pLinkJob->jobHi);
	SJobQ_Sche(pJobQHi, ppJobNode);
	if(!SJOBQ_ISEMPTY(pJobQHi)){
		goto __LABEL_IOLINK_REDO;
	}

	pJobQLo 	= &(pLinkJob->jobLo);
	SJobQ_Sche(pJobQLo, ppJobNode);
	if(!SJOBQ_ISEMPTY(pJobQHi)){
		goto __LABEL_IOLINK_REDO;
	}
	
	if(_ioLink_XfReady(pLinkJob) != 0){
		goto __LABEL_IOLINK_REDO;
	}

	return 0;
}


int 	ioLink_Dump(_t_ioLinkJob * pLinkJob)
{
	_t_rsiQue * pRsi;

	if(pLinkJob){
		/* Link Stat */
		printf("LinkStat: %lu\n", pLinkJob->linkStat);

		/* Queue */
		pRsi = &(pLinkJob->txQ);
		printf("TxQ: %ld - %ld\n", pRsi->ready.count, pRsi->stdby.count);

		pRsi = &(pLinkJob->rxQ);
		printf("RxQ: %ld - %ld\n", pRsi->ready.count, pRsi->stdby.count);
	}

	return -1;	
}
